package common

import (
	"crypto/rand"
	"errors"
	"fmt"
	"time"
)

type Session string

var (
	sessions             = make(map[Session]map[string]interface{})
	sessionTimeout int64 = 3600 //default expire session after 1 hour
)

func NewSession() Session {
	sid, err := generateSessionId()
	if err != nil {
		Application.SendMail(fmt.Sprintf("generateSessionId Error: %v\n", err))
		panic(err)
	}
	sess := SessionFromId(sid)
	sessions[sess] = make(map[string]interface{})
	return sess
}

func SessionFromId(sid string) Session {
	return Session(sid)
}

func (s Session) RemoveSession() {
	delete(sessions, s)
}

func (s Session) Exists() bool {
	_, ok := sessions[s]
	return ok
}

func (s Session) Put(name string, value interface{}) error {
	sobj, ok := sessions[s]
	if !ok {
		return errors.New("Session does not exist.")
	}

	sobj[name] = value
	return nil
}

func (s Session) Get(name string) (interface{}, error) {
	sobj, ok := sessions[s]
	if !ok {
		return nil, errors.New("Session does not exist.")
	}

	v, ok := sobj[name]
	if !ok {
		return nil, errors.New("No such key.")
	}

	return v, nil
}

func (s Session) UpdateSession() {
	s.Put("timeAccessed", time.Now().Unix())
}

func SessionGC() {
	for sess, _ := range sessions {
		timeacc, _ := sess.Get("timeAccessed")
		if timeacc.(int64)+sessionTimeout < time.Now().Unix() {
			sess.RemoveSession()
		}
	}
	time.AfterFunc(time.Duration(600)*time.Second, func() { SessionGC() }) // 600秒，即10分钟清理一次
}

func generateSessionId() (string, error) {
	bytes := make([]byte, 32)
	if _, err := rand.Read(bytes); err != nil {
		return "", err
	}

	return string(bytes), nil
}
